import requests
import time
import hashlib
import hmac
import json
from decimal import Decimal


class BinanceTrader:
    def __init__(self, api_key, api_secret, testnet=True):
        self.api_key = api_key
        self.api_secret = api_secret
        self.base_url = "https://testnet.binance.vision" if testnet else "https://api.binance.com"
        self.time_offset = 0
        self._sync_time()

    def _sync_time(self):
        """同步服务器时间"""
        try:
            response = requests.get(f"{self.base_url}/api/v3/time")
            server_time = response.json()['serverTime']
            local_time = int(time.time() * 1000)
            self.time_offset = server_time - local_time
        except Exception as e:
            print(f"时间同步失败: {e}")

    def _get_timestamp(self):
        return int(time.time() * 1000) + self.time_offset

    def _create_signature(self, params):
        return hmac.new(
            self.api_secret.encode('utf-8'),
            params.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()

    def _make_signed_request(self, method, endpoint, params=None):
        """发送签名请求"""
        timestamp = self._get_timestamp()

        # 构建查询参数
        query_string = f"timestamp={timestamp}&recvWindow=5000"
        if params:
            query_string = f"{params}&{query_string}"

        signature = self._create_signature(query_string)
        url = f"{self.base_url}{endpoint}?{query_string}&signature={signature}"

        headers = {"X-MBX-APIKEY": self.api_key}

        try:
            if method.upper() == "GET":
                response = requests.get(url, headers=headers)
            else:
                response = requests.post(url, headers=headers)

            return response.json()
        except Exception as e:
            return {"error": str(e)}

    def get_symbol_info(self, symbol="BNBUSDT"):
        """获取交易对信息"""
        endpoint = "/api/v3/exchangeInfo"
        response = requests.get(f"{self.base_url}{endpoint}?symbol={symbol}")

        if 'symbols' in response.json():
            for s in response.json()['symbols']:
                if s['symbol'] == symbol:
                    return s
        return None

    def get_current_price(self, symbol="BNBUSDT"):
        """获取当前价格"""
        endpoint = "/api/v3/ticker/price"
        response = requests.get(f"{self.base_url}{endpoint}?symbol={symbol}")
        return response.json()

    def get_min_notional(self, symbol="BNBUSDT"):
        """获取最小交易金额"""
        symbol_info = self.get_symbol_info(symbol)
        if symbol_info:
            for filter_item in symbol_info['filters']:
                if filter_item['filterType'] == 'MIN_NOTIONAL':
                    return Decimal(filter_item['minNotional'])
        return Decimal('10.0')  # 默认10 USDT

    def get_lot_size(self, symbol="BNBUSDT"):
        """获取数量精度"""
        symbol_info = self.get_symbol_info(symbol)
        if symbol_info:
            for filter_item in symbol_info['filters']:
                if filter_item['filterType'] == 'LOT_SIZE':
                    return {
                        'minQty': Decimal(filter_item['minQty']),
                        'stepSize': Decimal(filter_item['stepSize'])
                    }
        return {'minQty': Decimal('0.001'), 'stepSize': Decimal('0.001')}

    def get_precision(self, symbol="BNBUSDT"):
        """获取价格精度"""
        symbol_info = self.get_symbol_info(symbol)
        if symbol_info:
            return symbol_info['quotePrecision']
        return 8

    def format_quantity(self, quantity, step_size):
        """格式化数量到正确精度"""
        precision = abs(Decimal(str(step_size)).as_tuple().exponent)
        return Decimal(quantity).quantize(Decimal('1.' + '0' * precision))

    def buy_min_bnb(self, usdt_amount=None):
        """购买最小单位的BNB"""
        symbol = "BNBUSDT"

        # 获取当前价格
        price_info = self.get_current_price(symbol)
        current_price = Decimal(price_info['price'])
        print(f"当前BNB价格: {current_price} USDT")

        # 获取最小交易金额
        min_notional = self.get_min_notional(symbol)
        print(f"最小交易金额: {min_notional} USDT")

        # 获取数量精度
        lot_size = self.get_lot_size(symbol)
        print(f"最小数量: {lot_size['minQty']}, 步长: {lot_size['stepSize']}")

        # 计算购买数量
        if usdt_amount is None:
            # 使用最小交易金额
            usdt_amount = min_notional

        quantity = usdt_amount / current_price
        quantity = self.format_quantity(quantity, lot_size['stepSize'])

        # 确保数量不小于最小数量
        if quantity < lot_size['minQty']:
            quantity = lot_size['minQty']

        # 确保交易金额不小于最小金额
        notional_value = quantity * current_price
        if notional_value < min_notional:
            # 调整数量以满足最小金额要求
            quantity = min_notional / current_price
            quantity = self.format_quantity(quantity, lot_size['stepSize'])

        print(f"计划购买数量: {quantity} BNB")
        print(f"交易金额: {quantity * current_price} USDT")

        # 确认交易
        confirm = input("确认执行购买？(y/n): ")
        if confirm.lower() != 'y':
            print("交易取消")
            return None

        # 执行市价单购买
        endpoint = "/api/v3/order"
        params = f"symbol={symbol}&side=BUY&type=MARKET&quantity={quantity}"

        result = self._make_signed_request("POST", endpoint, params)
        return result

    def get_account_balance(self, asset="BNB"):
        """获取账户余额"""
        endpoint = "/api/v3/account"
        result = self._make_signed_request("GET", endpoint)

        if 'balances' in result:
            for balance in result['balances']:
                if balance['asset'] == asset:
                    return balance
        return None


# 使用示例
if __name__ == "__main__":
    # 从环境变量获取API密钥（推荐）
    import os

    # API_KEY = os.getenv('BINANCE_API_KEY', 'your_api_key_here')
    # API_SECRET = os.getenv('BINANCE_API_SECRET', 'your_api_secret_here')
    API_KEY = "oMlYVBgANplqH7XilK0rzJ47r7p1R3MWoywVNBYOGvj5UHYXebQnxZjX9kLpdd5V"
    API_SECRET = "8587vRjV45QsPPXhlrn3vcBdUwQJFA6HIBqfaVbJO7UqhhqTcVrQCR4GEO6w4OtN"
    # 创建交易客户端（默认使用测试网）
    trader = BinanceTrader(API_KEY, API_SECRET, testnet=False)

    # 检查余额
    usdt_balance = trader.get_account_balance("USDT")
    if usdt_balance:
        print(f"当前USDT余额: {usdt_balance['free']}")

    # 购买最小单位的BNB
    try:
        result = trader.buy_min_bnb()
        if result and 'orderId' in result:
            print("购买成功!")
            print("订单详情:", json.dumps(result, indent=2))

            # 检查新余额
            time.sleep(2)  # 等待订单完成
            bnb_balance = trader.get_account_balance("BNB")
            if bnb_balance:
                print(f"购买后BNB余额: {bnb_balance['free']}")
        else:
            print("购买失败:", result)
    except Exception as e:
        print(f"错误: {e}")